import {
    __private,
    _decorator,
    Component,
    DynamicAtlasManager,
    gfx, js,
    macro,
    Node, Size, Sprite,
    SpriteFrame,
    Texture2D,
    UITransform,
    EventTarget
} from "cc";
import { EDITOR_NOT_IN_PREVIEW } from "cc/env";
const Filter = gfx.Filter;

interface Atlas {
    _texture: Texture2D
    _innerSpriteFrames: SpriteFrame[]
    insertSpriteFrame (spriteFrame: SpriteFrame): {
        x: number;
        y: number;
        texture: Texture2D;
    } | null

    destroy: () => void
}

declare module 'cc' {

    interface DynamicAtlasManager {
        _atlasIndex: number
        _atlases: Atlas[]
        newAtlas: () => Atlas
    }

    interface SpriteFrame {
        _originalManual: {
            texture: Texture2D;
            x: number;
            y: number;
        } | null;
    }
}


@_decorator.ccclass("ManualDynamicAtlasRender")
export class ManualDynamicAtlasRender extends Component {

    private _count: number = 0;

    protected start() {
        ManualDynamicAtlasManager.instance.eventTarget.on("reset", () => {
            this._count = ManualDynamicAtlasManager.instance.atlasCount;
            this.updateSprites();
        });
    }

    protected update(dt: number) {
        if (this._count != ManualDynamicAtlasManager.instance.atlasCount) {
            this.updateSprites();
            this._count = ManualDynamicAtlasManager.instance.atlasCount;
        }
    }

    private updateSprites() {
        // 坑
        this.node.destroyAllChildren();
        this.node.removeAllChildren();

        const atlases = ManualDynamicAtlasManager.instance.atlases;
        console.log("DynamicAtlasManager atlas count:", ManualDynamicAtlasManager.instance.atlasCount);
        for (let atlas of atlases) {
            let node = new Node();
            node.layer = this.node.layer;
            let uiTransform = node.addComponent(UITransform);
            uiTransform.contentSize = new Size(750, 750);
            let sprite = node.addComponent(Sprite);
            sprite.sizeMode = Sprite.SizeMode.CUSTOM;
            sprite.type = Sprite.Type.SIMPLE;
            let spriteFrame = new SpriteFrame();
            spriteFrame.texture = atlas._texture;
            sprite.spriteFrame = spriteFrame;

            this.node.addChild(node);
        }
    }

}


export class ManualDynamicAtlasManager {
    public static instance: ManualDynamicAtlasManager;

    private _atlases: Atlas[] = [];
    private _atlasIndex = -1;
    private _maxAtlasCount = 5;

    private _eventTarget: EventTarget = new EventTarget();
    get eventTarget(): EventTarget {
        return this._eventTarget;
    }

    get atlases(): Readonly<Atlas[]> {
        return this._atlases;
    }

    get atlasCount (): number {
        return this._atlases.length;
    }

    reset() {
        for (let i = 0, l = this._atlases.length; i < l; i++) {
            const frames =  this._atlases[i]._innerSpriteFrames;
            for (let i = 0, l = frames.length; i < l; i++) {
                const frame = frames[i];
                if (!frame.isValid) {
                    continue;
                }
                frame._originalManual = null;
                frame._resetDynamicAtlasFrame();
            }

            this._atlases[i].destroy();
        }

        this._atlases.length = 0;
        this._atlasIndex = -1;

        this._eventTarget.emit("reset");
    }

    newAtlas() {
        let atlas = this._atlases[++this._atlasIndex];
        if (!atlas) {
            const originalIndex = DynamicAtlasManager.instance._atlasIndex;
            // 保证一定新建
            DynamicAtlasManager.instance._atlasIndex = DynamicAtlasManager.instance.atlasCount;
            atlas = DynamicAtlasManager.instance.newAtlas();
            DynamicAtlasManager.instance._atlasIndex = originalIndex;
            DynamicAtlasManager.instance._atlases.pop();
            this._atlases.push(atlas);
        }
        return atlas;
    }

    insertSpriteFrame(spriteFrame: SpriteFrame) {
        if (EDITOR_NOT_IN_PREVIEW) return null;

        if (!spriteFrame || spriteFrame._originalManual) return null;

        // if (!spriteFrame.packable) return null;

        // hack for pixel game,should pack to different sampler atlas
        const sampler = spriteFrame.texture.getSamplerInfo();
        if (sampler.minFilter !== Filter.LINEAR || sampler.magFilter !== Filter.LINEAR || sampler.mipFilter !== Filter.NONE) {
            return null;
        }

        let atlas = this._atlases[this._atlasIndex];
        if (!atlas) {
            atlas = this.newAtlas();
        }

        const frame = atlas.insertSpriteFrame(spriteFrame);
        if (!frame && this._atlasIndex !== this._maxAtlasCount) {
            atlas = this.newAtlas();
            return atlas.insertSpriteFrame(spriteFrame);
        }
        return frame;
    }

    public packToDynamicAtlas (frame: SpriteFrame): void {
        if (EDITOR_NOT_IN_PREVIEW) return;

        if (frame && !frame._originalManual && frame.texture && frame.texture.width > 0 && frame.texture.height > 0) {
            const packedFrame = this.insertSpriteFrame(frame);
            if (packedFrame) {
                frame._originalManual = packedFrame;
                frame._setDynamicAtlasFrame(packedFrame);
            }
        }
    }

    packAllToDynamicAtlas(node: Node) {
        for (let sprite of node.getComponents(Sprite)) {
            ManualDynamicAtlasManager.instance.packToDynamicAtlas(sprite.spriteFrame);
        }
        const sprites = node.getComponentsInChildren(Sprite);
        for (let sprite of sprites) {
            ManualDynamicAtlasManager.instance.packToDynamicAtlas(sprite.spriteFrame);
        }
    }
}

export const manualDynamicAtlasManager: ManualDynamicAtlasManager = ManualDynamicAtlasManager.instance = new ManualDynamicAtlasManager();
